﻿using System;
using System.Collections.Generic;
using System.Data.Services.Common;
using System.Linq;
using System.Xml.Linq;
using umbraco.DataServices.Query;

namespace umbraco.DataServices.Linq
{
    /// <summary>
    /// Lazy Loaded Strongly Typed Umbraco Node
    /// </summary>
    [EntityPropertyMapping("Title", SyndicationItemProperty.Title, SyndicationTextContentKind.Plaintext, true)]
    public class UmbracoNode : IDynamicProperty, INodeNavigator {

        public UmbracoNode(UmbracoContext context, XElement element, IEnumerable<string> targetAlias) {
            if (context == null)
                throw new ArgumentNullException("context");
            if (element == null)
                throw new ArgumentNullException("element");
            Element = element;
            Context = context;
            TargetAlias = targetAlias;
            Properties = new UmbracoPropertyCollection(this);
        }

        public UmbracoNode(UmbracoContext context, XElement element, string targetAlias)
        {
            if (context == null)
                throw new ArgumentNullException("context");
            if (element == null)
                throw new ArgumentNullException("element");
            Element = element;
            Context = context;
            TargetAlias = new[] { targetAlias };
            Properties = new UmbracoPropertyCollection(this);
        }

        public XElement Element { get; private set; }
        public UmbracoContext Context {get; private set;}
        public IEnumerable<string> TargetAlias { get; private set; }

        /// <summary>
        /// Used As An Override For Linq To Objects For Dynamic Properties
        /// The QueryProvider Uses This Dynamically To Get Object Values For All Properties
        /// </summary>
        public object GetPropertyValue(string propertyName) {
            return Properties.GetValue(propertyName);
        }
        
        public int Id {
            get { return new Lazy<int>(() => Context.Schema.GetId(Element)).Value; }
        }

        public int ParentId {
            get { return new Lazy<int>(() => Context.Schema.GetParentId(Element)).Value; }
        }

        public int Level {
            get { return new Lazy<int>(() => Context.Schema.GetLevel(Element)).Value; }
        }

        public int WriterId {
            get { return new Lazy<int>(() => Context.Schema.GetWriterId(Element)).Value; }
        }

        public int CreatorId {
            get { return new Lazy<int>(() => Context.Schema.GetCreatorId(Element)).Value; }
        }

        public int TemplateId {
            get { return new Lazy<int>(() => Context.Schema.GetTemplateId(Element)).Value; }
        }

        public int SortOrder {
            get { return new Lazy<int>(() => Context.Schema.GetSortOrder(Element)).Value; }
        }

        public DateTime CreateDate {
            get { return new Lazy<DateTime>(() => Context.Schema.GetCreateDate(Element)).Value; }
        }

        public DateTime UpdateDate {
            get { return new Lazy<DateTime>(() => Context.Schema.GetUpdateDate(Element)).Value; }
        }

        public string Title {
            get { return new Lazy<string>(() => Context.Schema.GetTitle(Element)).Value; }
        }

        public string UrlName {
            get { return new Lazy<string>(() => Context.Schema.GetUrlName(Element)).Value; }
        }

        public int DocumentTypeId {
            get { return new Lazy<int>(() => Context.Schema.GetDocumentTypeId(Element)).Value; }
        }

        public string DocumentType {
            get { return new Lazy<string>(() => Context.Schema.GetDocumentType(Element)).Value; }
        }
        
        public string WriterName {
            get { return new Lazy<string>(() => Context.Schema.GetWriterName(Element)).Value; }
        }

        public string CreatorName {
            get { return new Lazy<string>(() => Context.Schema.GetCreatorName(Element)).Value; }
        }

        public UmbracoNode Parent {
            get {
                var parent = Context.Schema.GetParent(Element);
                if (parent == null)
                    return null;
                return new UmbracoNode(Context, Element.Parent, TargetAlias);
            }
        }

        public IEnumerable<UmbracoNode> Ancestors {
            get { return new NodeIterator(Context, Context.Schema.GetAncestors(Element), TargetAlias); }
        }

        public IEnumerable<UmbracoNode> AncestorsNamed(string alias) {
            var inheritedAliases = Context.GetDescendantDocumentTypes(alias);
            return new NodeIterator(Context, Context.Schema.GetAncestorsNamed(Element, inheritedAliases), TargetAlias);
        }

        public IEnumerable<UmbracoNode> Descendants {
            get { return new NodeIterator(Context, Context.Schema.GetDescendants(Element), TargetAlias); }
        }

        public IEnumerable<UmbracoNode> DescendantsNamed(string alias) {
            var inheritedAliases = Context.GetDescendantDocumentTypes(alias);
            return new NodeIterator(Context, Context.Schema.GetDescendantsNamed(Element, inheritedAliases), TargetAlias);
        }

        public IEnumerable<UmbracoNode> Sibblings {
            get { return new NodeIterator(Context, Context.Schema.GetSibblings(Element), TargetAlias); }
        }

        public IEnumerable<UmbracoNode> SibblingsNamed(string alias) {
            var inheritedAliases = Context.GetDescendantDocumentTypes(alias);
            return new NodeIterator(Context, Context.Schema.GetSibblingsNamed(Element, inheritedAliases), TargetAlias);
        }

        public IEnumerable<UmbracoNode> Children {
            get { return new NodeIterator(Context, Context.Schema.GetChildren(Element), TargetAlias); }
        }

        public IEnumerable<UmbracoNode> ChildrenNamed(string alias) {
            var inheritedAliases = Context.GetDescendantDocumentTypes(alias);
            return new NodeIterator(Context, Context.Schema.GetChildrenNamed(Element, inheritedAliases), TargetAlias);
        }

        public bool IsInherited { get { return true; } } //TODO...

        public UmbracoPropertyCollection Properties { get; set; }
    }
}
